const webpack = require('webpack');
const path = require('path');
const TransferWebpackPlugin = require('transfer-webpack-plugin');
const autoprefixer = require('autoprefixer');
const debug = require('debug')('app:config:webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
// Default Configuration
const projectConfig = {
  env: process.env.NODE_ENV || 'development',

  // Project Structure
  basePath: path.resolve(__dirname, './'),
  dirClient: 'src',
  dirDist: 'dist',
  dirPublic: 'www',
  dirServer: 'server',
  dirTests: 'tests',

  // Server Configuration
  // use string 'localhost' to prevent exposure on local network
  serverHost : 'localhost', 
  serverPort : process.env.PORT || 3333,

  // Compiler Configuration
  compilerDevtool: 'source-map',
  compilerPublicPath: '/',
  compilerHashType: 'hash',
  compilerFailOnWarning: false,
  compilerQuiet: false,
  compilerStats: {
    chunks: false,
    chunkModules: false,
    colors: true
  },
  compilerBabel:  {
    cacheDirectory: true,
    plugins: ['transform-runtime'],
    presets: ['es2015', 'react', 'stage-1']
  },
  compilerVendors: [
    'react',
    'react-redux',
    'react-router',
    'redux'
  ]
};
// Environment
// N.B.: globals added here must _also_ be added to .eslintrc
projectConfig.globals = {
  'process.env': {
    'NODE_ENV': JSON.stringify(projectConfig.env)
  },
  'NODE_ENV': projectConfig.env,
  '__DEV__': projectConfig.env === 'development',
  '__PROD__': projectConfig.env === 'production',
  '__TEST__': projectConfig.env === 'test',
  // '__COVERAGE__': !argv && !argv.watch && projectConfig.env === 'test',
  '__BASENAME__': JSON.stringify(process.env.BASENAME || '')
};

// Validate Vendor Dependencies
const pkg = require('./package.json');

projectConfig.compilerVendors = projectConfig.compilerVendors
  .filter((dep) => {
    if (pkg.dependencies[dep]) return true;

    debug(
      `Package "${dep}" was not found as an npm dependency in package.json; ` +
      `it won't be included in the webpack vendor bundle.
       Consider removing it from \`compiler_vendors\` in ~/config/index.js`
    );
  });

// Utilities
function base () {
  const args = [projectConfig.basePath].concat([].slice.call(arguments));
  return path.resolve.apply(path, args);
}

projectConfig.paths = {
  base   : base,
  client : base.bind(null, projectConfig.dirClient),
  public : base.bind(null, projectConfig.dirPublic),
  dist   : base.bind(null, projectConfig.dirDist)
};

const config = {
  name    : 'client',
  target  : 'web',
  devtool : projectConfig.compilerDevtool,
  resolve : {
    root       : projectConfig.paths.client(),
    extensions : ['', '.js', '.jsx', '.json']
  },
  module : {}
};

// Entry Points
const APP_ENTRY = projectConfig.paths.client('app.js');
// Entry points to the project 入口文件
/*config.entry = {
  app: projectConfig.globals.__DEV__
    ? [APP_ENTRY].concat(`webpack-hot-middleware/client?path=${projectConfig.compilerPublicPath}__webpack_hmr`)
    : [APP_ENTRY],
  vendor: projectConfig.compilerVendors
};*/

config.entry = [
  'whatwg-fetch',
  'webpack/hot/dev-server',
  'webpack/hot/only-dev-server',
  APP_ENTRY
]

// Bundle Output
config.output = {
  filename   : `[name].[${projectConfig.compilerHashType}].js`,
  path       : projectConfig.paths.dist(),
  publicPath : projectConfig.compilerPublicPath
}

// Server Configuration options server 配置
config.devServer = {
  // Relative directory for base of server
  contentBase: 'www', 
  devtool: projectConfig.compilerDevtool,
  // Live-reload
  hot: true, 
  inline: true,
  // Port Number
  port: projectConfig.serverPort, 
  // Change to '0.0.0.0' for external facing server
  host: projectConfig.serverHost 
}

// Externals
config.externals = {};
config.externals['react/lib/ExecutionEnvironment'] = true;
config.externals['react/lib/ReactContext'] = true;
config.externals['react/addons'] = true;

// Plugins
config.plugins = [
  // 移动文件,如果发布目录和编辑目录不一致时,可以配置此项将编辑的 www 内容文件转移到发布目录
  /*new TransferWebpackPlugin([
    {from: 'www'},
  ], path.resolve(__dirname, './')),*/
  new webpack.DefinePlugin(projectConfig.globals),
  new HtmlWebpackPlugin({
    template : projectConfig.paths.public('index.html'),
    hash     : false,
    // favicon  : projectConfig.paths.public('favicon.ico'),
    filename : 'index.html',
    inject   : 'body',
    minify   : {
      collapseWhitespace : true
    }
  })
];

// Ensure that the compiler exits on errors during testing so that
// they do not get skipped and misreported.
if (projectConfig.globals.__TEST__ && !argv.watch) {
  config.plugins.push(function () {
    this.plugin('done', function (stats) {
      if (stats.compilation.errors.length) {
        // Pretend no assets were generated. This prevents the tests
        // from running making it clear that there were warnings.
        throw new Error(
          stats.compilation.errors.map(err => err.message || err)
        )
      }
    })
  });
}

if (projectConfig.globals.__DEV__) {
  debug('Enabling plugins for live development (HMR, NoErrors).');
  // Enables Hot Modules Replacement
  // Allows error warnings but does not stop compiling.
  config.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  );
} else if (projectConfig.globals.__PROD__) {
  debug('Enabling plugins for production (OccurenceOrder, Dedupe & UglifyJS).');
  config.plugins.push(
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.optimize.DedupePlugin(),
    // 压缩
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        unused: true,
        dead_code: true,
        warnings: false
      }
    })
  );
}

// Don't split bundles during testing, since we only want import one bundle
if (!projectConfig.globals.__TEST__) {
  config.plugins.push(
    new webpack.optimize.CommonsChunkPlugin({
      names : ['vendor']
    })
  );
}

// Loaders

// JavaScript / JSON
config.module.loaders = [{
  test    : /\.(js|jsx)$/,
  exclude : /node_modules/,
  loader  : 'babel',
  query   : projectConfig.compilerBabel
}, {
  test   : /\.json$/,
  loader : 'json'
}]

// Style Loaders
// We use cssnano with the postcss loader, so we tell
// css-loader not to duplicate minimization.
// const BASE_CSS_LOADER = 'css?sourceMap&-minimize';
const BASE_CSS_LOADER = 'css?sourceMap';

/*config.module.loaders.push({
  test    : /\.scss$/,
  exclude : null,
  loaders : [
    'style',
    BASE_CSS_LOADER,
    'postcss',
    'sass?sourceMap'
  ]
})*/
config.module.loaders.push({
  test    : /\.css$/,
  exclude : null,
  loaders : [
    'style',
    BASE_CSS_LOADER,
    'postcss'
  ]
});

/*config.sassLoader = {
  includePaths : projectConfig.paths.client('styles')
}*/

config.postcss = [
  /*cssnano({
    autoprefixer : {
      add      : true,
      remove   : true,
      browsers : ['last 2 versions']
    },
    discardComments : {
      removeAll : true
    },
    discardUnused : false,
    mergeIdents   : false,
    reduceIdents  : false,
    safe          : true,
    sourcemap     : true
  })*/
  autoprefixer({
    add: true,
    remove: true,
    browsers: ['last 2 versions']
  })
];


// File loaders
/* eslint-disable */
config.module.loaders.push(
  { test: /\.woff(\?.*)?$/,  loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff' },
  { test: /\.woff2(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2' },
  { test: /\.otf(\?.*)?$/,   loader: 'file?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype' },
  { test: /\.ttf(\?.*)?$/,   loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream' },
  { test: /\.eot(\?.*)?$/,   loader: 'file?prefix=fonts/&name=[path][name].[ext]' },
  { test: /\.svg(\?.*)?$/,   loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml' },
  { test: /\.(png|jpg)$/,    loader: 'url?limit=8192' },
  {
    test: /\.style.js$/,
    loader: 'style-loader!css-loader?modules&importLoaders=1!postcss-loader?parser=postcss-js!babel'
  }
);
/* eslint-enable */

// ------------------------------------
// Finalize Configuration
// ------------------------------------
// when we don't know the public path (we know it only when HMR is enabled [in development]) we
// need to use the extractTextPlugin to fix this issue:
// http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809
if (!projectConfig.globals.__DEV__) {
  debug('Applying ExtractTextPlugin to CSS loaders.');
  config.module.loaders.filter((loader) =>
    loader.loaders && loader.loaders.find((name) => /css/.test(name.split('?')[0]))
  ).forEach((loader) => {
    const first = loader.loaders[0];
    const rest = loader.loaders.slice(1);
    loader.loader = ExtractTextPlugin.extract(first, rest.join('!'));
    delete loader.loaders;
  })

  config.plugins.push(
    new ExtractTextPlugin('[name].[contenthash].css', {
      allChunks : true
    })
  );
}

module.exports = config;
